package persistence

import (
	"context"
	"fmt"
	"strings"
	"time"

	"baseService/internal/domain/repositories"
	"baseService/internal/pkg/errors"

	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/driver/sqlserver"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// DatabaseConfig 数据库配置
type DatabaseConfig struct {
	Driver          string        `json:"driver"`
	Host            string        `json:"host"`
	Port            int           `json:"port"`
	Database        string        `json:"database"`
	Username        string        `json:"username"`
	Password        string        `json:"password"`
	Charset         string        `json:"charset"`
	MaxIdleConns    int           `json:"max_idle_conns"`
	MaxOpenConns    int           `json:"max_open_conns"`
	ConnMaxLifetime time.Duration `json:"conn_max_lifetime"`
	LogLevel        string        `json:"log_level"`
}

// GormRepository GORM仓储实现
type GormRepository struct {
	db *gorm.DB
}

// NewGormRepository 创建GORM仓储
func NewGormRepository(config *DatabaseConfig) (repositories.Repository, error) {
	db, err := initGormDB(config)
	if err != nil {
		return nil, fmt.Errorf("初始化数据库连接失败: %w", err)
	}

	return &GormRepository{db: db}, nil
}

// initGormDB 初始化GORM数据库连接
func initGormDB(config *DatabaseConfig) (*gorm.DB, error) {
	var dialector gorm.Dialector

	switch strings.ToLower(config.Driver) {
	case "mysql":
		dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
			config.Username, config.Password, config.Host, config.Port, config.Database, config.Charset)
		dialector = mysql.Open(dsn)
	case "postgres", "postgresql":
		dsn := fmt.Sprintf("host=%s port=%d user=%s dbname=%s password=%s sslmode=disable TimeZone=Asia/Shanghai",
			config.Host, config.Port, config.Username, config.Database, config.Password)
		dialector = postgres.Open(dsn)
	case "sqlserver":
		dsn := fmt.Sprintf("server=%s;port=%d;database=%s;user id=%s;password=%s;encrypt=disable",
			config.Host, config.Port, config.Database, config.Username, config.Password)
		dialector = sqlserver.Open(dsn)
	default:
		return nil, fmt.Errorf("不支持的数据库驱动: %s", config.Driver)
	}

	// 配置GORM
	gormConfig := &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true,
		Logger:                                   getGormLogger(config.LogLevel),
	}

	db, err := gorm.Open(dialector, gormConfig)
	if err != nil {
		return nil, fmt.Errorf("连接数据库失败: %w", err)
	}

	// 配置连接池
	sqlDB, err := db.DB()
	if err != nil {
		return nil, fmt.Errorf("获取SQL DB失败: %w", err)
	}

	sqlDB.SetMaxIdleConns(config.MaxIdleConns)
	sqlDB.SetMaxOpenConns(config.MaxOpenConns)
	sqlDB.SetConnMaxLifetime(config.ConnMaxLifetime)

	return db, nil
}

// getGormLogger 获取GORM日志器
func getGormLogger(level string) logger.Interface {
	var logLevel logger.LogLevel
	switch strings.ToLower(level) {
	case "silent":
		logLevel = logger.Silent
	case "error":
		logLevel = logger.Error
	case "warn":
		logLevel = logger.Warn
	case "info":
		logLevel = logger.Info
	default:
		logLevel = logger.Warn
	}

	return logger.Default.LogMode(logLevel)
}

// Example 返回示例仓储 - 演示如何添加新的仓储
func (r *GormRepository) Example() repositories.ExampleRepository {
	return NewExampleRepository(r.db)
}

// Begin 开始事务
func (r *GormRepository) Begin(ctx context.Context) (repositories.Transaction, error) {
	tx := r.db.WithContext(ctx).Begin()
	if tx.Error != nil {
		return nil, errors.NewDatabaseError("开始事务失败", tx.Error)
	}

	return &gormTransaction{tx: tx}, nil
}

// WithTransaction 在事务中执行操作
func (r *GormRepository) WithTransaction(ctx context.Context, fn func(tx repositories.Transaction) error) error {
	tx, err := r.Begin(ctx)
	if err != nil {
		return err
	}

	defer func() {
		if p := recover(); p != nil {
			tx.Rollback()
			panic(p)
		}
	}()

	if err := fn(tx); err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit()
}

// gormTransaction GORM事务实现
type gormTransaction struct {
	tx *gorm.DB
}

func (t *gormTransaction) Commit() error {
	if err := t.tx.Commit().Error; err != nil {
		return errors.NewDatabaseError("提交事务失败", err)
	}
	return nil
}

func (t *gormTransaction) Rollback() error {
	if err := t.tx.Rollback().Error; err != nil {
		return errors.NewDatabaseError("回滚事务失败", err)
	}
	return nil
}

func (t *gormTransaction) Repository() repositories.Repository {
	return &GormRepository{db: t.tx}
}

func (t *gormTransaction) Raw() interface{} {
	return t.tx
}

// GetDB 获取原始的GORM数据库连接
func (r *GormRepository) GetDB() *gorm.DB {
	return r.db
}
